#ifndef __PROCESS_POOL_HPP__
#define __PROCESS_POOL_HPP__

#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <functional>
#include <sys/wait.h>
#include "Task.hpp"
#include <ctime>

const int gdefault_process_num = 5;

// 等价typedef std::function<void (int fd)> func_t;
using callback_t = std::function<void(int fd)>;

// 先描述
class Channel
{
public:
	Channel()
	{
	}
	Channel(int fd, const std::string &name, pid_t id) : _wfd(fd), _name(name), _sub_target(id)
	{
	}
	void DebugPrint()
	{
		printf("channel name: %s, wfd: %d, terget pid: %d\n", _name.c_str(), _wfd, _sub_target);
	}
	~Channel() {}
	int Fd() { return _wfd; }
	std::string Name() { return _name; }
	pid_t Target() { return _sub_target; }
	void Close() { close(_wfd); }
	void Wait()
	{
		pid_t rid = waitpid(_sub_target, nullptr, 0);
		(void)rid;
	}

private:
	int _wfd;
	std::string _name;
	pid_t _sub_target; // 目标子进程是谁
	int _load;		   // 负载
};

// 父进程
class ProcessPool
{
private:
	void CtrlSubProcessHeler(int &index)
	{
		// 1. 选择一个通道（进程）
		int who = index;
		index++;
		index %= _channels.size();

		// 2. 选择一个任务，随机
		int x = rand() % tasks.size(); // [0, 3]

		// 3. 任务推送给子进程
		std::cout << "选择信道：" << _channels[who].Name() << ", subtarget: " << _channels[who].Target() << std::endl;
		write(_channels[who].Fd(), &x, sizeof(x));
		sleep(2);
	}
public:
	ProcessPool(int num = gdefault_process_num) : _processnum(num)
	{
		srand(time(nullptr) ^ getpid() ^ 0x777);
	}
	~ProcessPool()
	{
	}
	bool InitProcessPool(callback_t cb) // 所有信道
	{
		for (int i = 0; i < _processnum; i++)
		{
			// 1. 创建管道（先有管道才能让父子间进行通信）
			int pipefd[2] = {0};
			int n = pipe(pipefd);
			if (n < 0)
				return false;

			// 2. 创建子进程
			pid_t id = fork();
			if (id < 0)
				return false;
			if (id == 0)
			{ 
				// 子进程除了要关闭自己的w，同时也要关闭，自己从父进程那里继承下来的所有的之前进程w端
				//我的子进程，要关闭的，从父进程那里继承下来的wfd都在哪里？？
				// _channels本身是被子进程继承下去的，
				// 1. 子进程不用担心，父进程会影响自己的_channels(子进程继承下来的_channels,
				//永远都会保存之前进程所有的写端描述符，不会受后续操作的影响)
				// 2. fork之后，当前进程，只会看到所有的历史进程的wfd，并不受后续父进程emplace_backd的影响
				std::cout << "进程：" << getpid() << "关闭了：";
				for(auto& c : _channels)
				{
					c.Close();//这样就ok了
					std::cout << c.Fd() << " ";
				}
				std::cout << std::endl;

				// child read
				//  3. 关闭不需要的rw端，形成信道
				close(pipefd[1]);
				// 子进程在这里做事
				cb(pipefd[0]); // 回调

				exit(0);
			}

			// father，write
			close(pipefd[0]);
			std::string name = "channel-" + std::to_string(i);
			_channels.emplace_back(pipefd[1], name, id);
			// Channel channel(pipefd[1], name, id);
			// chanenls.push_back(channel);
			sleep(1);
		}
		return true;
	}

	// // 1. 创建进程池，让子进程进入自己的工作流中，父进程得到channels
	// InitProcessPool(channels, [](int fd) { // lambda
	// 	while (true)
	// 	{
	// 		int code = 0;
	// 		// std::cout << "子进程阻塞:" << getpid() << std::endl;
	// 		ssize_t n = read(fd, &code, sizeof(code));
	// 		if (n > 0)
	// 		{
	// 			std::cout << "子进程被唤醒:" << getpid() << std::endl;
	// 		}
	// 	}
	// });

	// for (auto c : channels)
	// {
	// 	c.DebugPrint();
	// }

	// sleep(3);
	// std::cout << "父进程开始控制" << std::endl;

	// 2. 控制唤醒指定的一个子进程，让孩子进程完成指定的任务
	// 2.1 轮询选择一个子进程(选择一个信道) -- 负载均衡

	

	void PollingCtrlSubProcess()
	{
		int index = 0;
		while (true)
		{
			CtrlSubProcessHeler(index);
		}
	}

	void PollingCtrlSubProcess(int count)
	{
		if (count < 0)
			return;
		int index = 0;
		while (count)
		{
			CtrlSubProcessHeler(index);
			count--;
		}
	}

	void RandomCtrlSubProcess()
	{
	}
	void LoadCtrlSubProcess()
	{
	}

	void ProcessPoolPrintFd()
	{
		std::cout << "进程池wfd list: ";
		for(auto& c : _channels)
		{
			std::cout << c.Fd() << " ";
		}
		std::cout << std::endl;
	}

	// 这里其实是有一个bug的！！
	void WaitSubProcesses()
	{
		//防止引用计数问题，可以选择倒着关
		// for(int end = _channels.size()-1; end >= 0; end--)
		// {
		// 	_channels[end].Close();
		// 	_channels[end].Wait();
		// }

		for(auto& c : _channels)
		{
			c.Close();
			c.Wait();              //不能这样写，引用计数有问题
		}

		// 1. 先让所有子进程结束
		// for (auto &c : _channels)
		// {
		// 	c.Close();
		// }
		// 2. 你再回收所有子进程僵尸状态
		// for (auto &c : _channels)
		// {
		// 	c.Wait();
		// 	std::cout << "回收子进程：" << c.Target() << std::endl;
		// }
	}

	// 父进程，父进程对子进程的控制，就会转化成对channels的使用
private:
	// 在组织
	std::vector<Channel> _channels; // 所有信道
	int _processnum;				// 有多少个子进程
};

#endif